import os
import re
import csv
import json
from collections import defaultdict, OrderedDict
from decimal import Decimal
import openpyxl
from openpyxl.chart import BarChart, Reference
from openpyxl.styles import Font, PatternFill
import logging
import datetime
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QLineEdit, QPushButton, QFileDialog,
                             QCheckBox, QComboBox, QTextEdit, QMessageBox,
                             QProgressBar, QDateEdit, QTableWidget, QTableWidgetItem,
                             QHeaderView, QGroupBox, QGridLayout, QFrame,
                             QMenu, QToolButton)
from PySide6.QtGui import QFont, QIcon, QAction
from PySide6.QtCore import Qt, QDate, QThread, Signal
import sys

# 配置日志
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")


def date_to_tuple(date_str):
    """日期排序辅助函数，支持多种日期格式"""
    try:
        if "年" in date_str and "月" in date_str and "日" in date_str:
            year, month, day = re.findall(r'\d+', date_str)
            return (int(year), int(month), int(day))
        else:
            month, day = re.findall(r'\d+', date_str)
            return (int(month), int(day))
    except:
        return (0, 0, 0)  # 无效日期置于最后


def parse_filename(filename, custom_pattern=None):
    """解析文件名，支持自定义正则表达式模式"""
    pattern = custom_pattern or r'^(\d{1,4}年\d{1,2}月\d{1,2}日|' \
                                r'\d{1,2}月\d{1,2}日)[_\-]' \
                                r'(\d+\.?\d*)元[_\-]([^\.]+)\.pdf$'
    match = re.match(pattern, filename)
    if not match:
        raise ValueError(f"无效的文件名格式: {filename}")
    return match.groups()


class ProcessingThread(QThread):
    """处理文件夹的工作线程，避免UI卡顿"""
    progress_updated = Signal(int, str)
    processing_finished = Signal(dict)
    error_occurred = Signal(str)

    def __init__(self, folder_path, custom_pattern, date_filter):
        super().__init__()
        self.folder_path = folder_path
        self.custom_pattern = custom_pattern
        self.date_filter = date_filter

    def run(self):
        try:
            records = []
            date_stats = defaultdict(list)
            person_stats = defaultdict(Decimal)
            errors = []
            total_files = 0
            processed_files = 0

            # 计算总文件数用于进度条
            for root, dirs, files in os.walk(self.folder_path):
                total_files += len([f for f in files if f.lower().endswith('.pdf')])

            if total_files == 0:
                self.processing_finished.emit({
                    "records": [],
                    "date_stats": {},
                    "person_stats": {},
                    "errors": ["未找到PDF文件"]
                })
                return

            # 处理文件
            for root, dirs, files in os.walk(self.folder_path):
                pdf_files = [f for f in files if f.lower().endswith('.pdf')]
                for f in pdf_files:
                    file_path = os.path.join(root, f)
                    try:
                        date, amount, name = parse_filename(f, self.custom_pattern)
                        amount = Decimal(amount)

                        # 应用日期过滤
                        if self.date_filter and not self.date_filter(date):
                            continue

                        records.append((date, amount, name))
                        date_stats[date].append(amount)
                        person_stats[name] += amount

                    except ValueError as ve:
                        errors.append(f"文件 {file_path} 格式错误: {str(ve)}")
                    except Exception as e:
                        errors.append(f"文件 {file_path} 处理失败: {str(e)}")

                    processed_files += 1
                    percentage = int(processed_files / total_files * 100)
                    self.progress_updated.emit(percentage, f"处理中: {processed_files}/{total_files}")
                    self.msleep(10)  # 释放CPU资源

            # 排序记录
            sorted_records = sorted(records, key=lambda x: date_to_tuple(x[0]))

            self.processing_finished.emit({
                "records": sorted_records,
                "date_stats": date_stats,
                "person_stats": person_stats,
                "errors": errors
            })

        except Exception as e:
            self.error_occurred.emit(f"处理过程中发生错误: {str(e)}")


def create_excel_report(data, output_path, include_charts=True):
    """生成标准格式Excel报表，支持图表生成"""
    wb = openpyxl.Workbook()

    # 发票明细页
    ws1 = wb.active
    ws1.title = "发票明细"
    headers = ["日期", "金额(元)", "报销人"]
    ws1.append(headers)

    # 设置表头样式
    for cell in ws1[1]:
        cell.font = Font(bold=True)
        cell.fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")

    # 写入明细数据
    for row_idx, row in enumerate(data["records"], start=2):
        ws1.append([row[0], f"{row[1]:.2f}", row[2]])
        # 交替行颜色，提高可读性
        if row_idx % 2 == 0:
            for cell in ws1[row_idx]:
                cell.fill = PatternFill(start_color="F0F0F0", end_color="F0F0F0", fill_type="solid")

    # 统计汇总页
    ws2 = wb.create_sheet("统计汇总")

    # 日期统计
    ws2.append(["日期统计"])
    ws2.append(["日期", "发票数量", "总金额(元)"])
    date_rows = []
    for date in sorted(data["date_stats"], key=date_to_tuple):
        amounts = data["date_stats"][date]
        date_rows.append([date, len(amounts), sum(amounts)])
        ws2.append([date, len(amounts), f"{sum(amounts):.2f}"])

    # 人员统计
    ws2.append([])
    ws2.append(["人员统计"])
    ws2.append(["姓名", "总金额(元)"])
    person_rows = []
    for name, total in sorted(data["person_stats"].items(), key=lambda x: -x[1]):
        person_rows.append([name, total])
        ws2.append([name, f"{total:.2f}"])

    # 设置列宽
    ws1.column_dimensions['A'].width = 18
    ws1.column_dimensions['B'].width = 15
    ws1.column_dimensions['C'].width = 15

    ws2.column_dimensions['A'].width = 18
    ws2.column_dimensions['B'].width = 15
    ws2.column_dimensions['C'].width = 15

    # 添加图表
    if include_charts and (date_rows or person_rows):
        ws3 = wb.create_sheet("数据可视化")

        # 日期统计柱状图
        if date_rows:
            ws3.append(["日期统计图表"])
            for row in date_rows:
                ws3.append(row)

            chart1 = BarChart()
            chart1.title = "各日期发票金额统计"
            chart1.x_axis.title = "日期"
            chart1.y_axis.title = "总金额(元)"

            data_ref = Reference(ws3, min_col=3, min_row=2, max_row=len(date_rows) + 1)
            cats_ref = Reference(ws3, min_col=1, min_row=2, max_row=len(date_rows) + 1)

            chart1.add_data(data_ref, titles_from_data=True)
            chart1.set_categories(cats_ref)
            ws3.add_chart(chart1, "E2")

        # 人员统计柱状图
        if person_rows:
            start_row = 8 if date_rows else 2
            ws3.append([])
            ws3.append(["人员统计图表"])
            for row in person_rows:
                ws3.append(row)

            chart2 = BarChart()
            chart2.title = "各人员报销金额统计"
            chart2.x_axis.title = "姓名"
            chart2.y_axis.title = "总金额(元)"

            data_ref = Reference(ws3, min_col=2, min_row=start_row + 2, max_row=start_row + 1 + len(person_rows))
            cats_ref = Reference(ws3, min_col=1, min_row=start_row + 2, max_row=start_row + 1 + len(person_rows))

            chart2.add_data(data_ref, titles_from_data=True)
            chart2.set_categories(cats_ref)
            ws3.add_chart(chart2, "E" + str(start_row + 2))

    wb.save(output_path)


def export_to_csv(data, output_path):
    """导出数据到CSV文件"""
    with open(output_path, 'w', newline='', encoding='utf-8-sig') as f:
        writer = csv.writer(f)
        writer.writerow(["日期", "金额(元)", "报销人"])
        for row in data["records"]:
            writer.writerow([row[0], f"{row[1]:.2f}", row[2]])


def export_to_json(data, output_path):
    """导出数据到JSON文件"""
    # 转换Decimal为float以便JSON序列化
    records = [{"date": row[0], "amount": float(row[1]), "name": row[2]} for row in data["records"]]
    date_stats = {date: [float(amt) for amt in amounts] for date, amounts in data["date_stats"].items()}
    person_stats = {name: float(total) for name, total in data["person_stats"].items()}

    output_data = {
        "records": records,
        "date_stats": date_stats,
        "person_stats": person_stats,
        "errors": data["errors"]
    }

    with open(output_path, 'w', encoding='utf-8') as f:
        json.dump(output_data, f, ensure_ascii=False, indent=2)


def open_folder(folder_path):
    """跨平台打开文件夹"""
    try:
        if os.name == "nt":
            os.startfile(folder_path)
        elif os.name == "posix":
            import subprocess
            subprocess.Popen(['open', folder_path])
    except Exception as e:
        logging.error(f"无法打开文件夹：{str(e)}")
        QMessageBox.warning(None, "警告", f"无法打开文件夹：{str(e)}")


class InvoiceProcessingTool(QWidget):
    def __init__(self):
        super().__init__()
        self.data = {"records": []}
        self.processing_thread = None
        self.initUI()

    def initUI(self):
        # 设置窗口标题和大小
        self.setWindowTitle("发票处理工具 v1.2")
        self.setGeometry(100, 100, 900, 750)

        # 设置中文字体
        default_font = QFont('Microsoft YaHei UI', 10)
        title_font = QFont('Microsoft YaHei UI', 12, QFont.Bold)
        button_font = QFont('Microsoft YaHei UI', 10, QFont.Bold)

        # 主布局
        main_layout = QVBoxLayout()

        # 样式表 - 移除深色模式相关代码
        self.light_stylesheet = """
            QWidget {
                background-color: #f8f9fa;
            }
            QLabel {
                color: #212529;
            }
            QLineEdit {
                padding: 5px;
                border: 1px solid #ced4da;
                border-radius: 4px;
                background-color: white;
            }
            QPushButton {
                background-color: #1976d2;
                color: white;
                padding: 6px 15px;
                border: none;
                border-radius: 4px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #1565c0;
            }
            QPushButton:pressed {
                background-color: #0d47a1;
            }
            QPushButton:disabled {
                background-color: #adb5bd;
                color: #6c757d;
            }
            QCheckBox, QComboBox {
                color: #212529;
                padding: 3px;
            }
            QGroupBox {
                border: 1px solid #ced4da;
                border-radius: 5px;
                margin-top: 10px;
                padding: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 3px;
            }
            QTextEdit {
                border: 1px solid #ced4da;
                border-radius: 4px;
                background-color: white;
                padding: 5px;
            }
            QTableWidget {
                gridline-color: #e9ecef;
                background-color: white;
            }
            QTableWidget::item {
                padding: 5px;
                border: none;
            }
            QTableWidget::item:selected {
                background-color: #bbdefb;
                color: #0d47a1;
            }
            QHeaderView::section {
                background-color: #e9ecef;
                padding: 6px;
                border: 1px solid #dee2e6;
                font-weight: bold;
            }
            QProgressBar {
                border: 1px solid #ced4da;
                border-radius: 4px;
                text-align: center;
                height: 16px;
            }
            QProgressBar::chunk {
                background-color: #1976d2;
            }
            QMenu {
                background-color: white;
                border: 1px solid #dee2e6;
                box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
            }
            QMenu::item {
                padding: 6px 20px;
            }
            QMenu::item:selected {
                background-color: #e3f2fd;
                color: #0d47a1;
            }
        """

        # 应用样式表
        self.setStyleSheet(self.light_stylesheet)

        # 顶部控制区
        top_layout = QVBoxLayout()

        # 文件夹选择区域
        select_layout = QHBoxLayout()
        folder_label = QLabel("选择发票文件夹:", self)
        folder_label.setFont(default_font)
        select_layout.addWidget(folder_label)

        self.folder_entry = QLineEdit(self)
        self.folder_entry.setFont(default_font)
        select_layout.addWidget(self.folder_entry)

        browse_button = QPushButton("浏览", self)
        browse_button.setFont(button_font)
        browse_button.clicked.connect(self.browse_folder)
        select_layout.addWidget(browse_button)

        # 导出路径选择
        output_label = QLabel("导出路径:", self)
        output_label.setFont(default_font)
        select_layout.addWidget(output_label)

        self.output_entry = QLineEdit(self)
        self.output_entry.setFont(default_font)
        select_layout.addWidget(self.output_entry)

        browse_output_button = QPushButton("浏览", self)
        browse_output_button.setFont(button_font)
        browse_output_button.clicked.connect(self.browse_output_folder)
        select_layout.addWidget(browse_output_button)

        # 文件数量统计
        self.file_count_label = QLabel("文件数量: 0", self)
        self.file_count_label.setFont(default_font)
        select_layout.addWidget(self.file_count_label)

        top_layout.addLayout(select_layout)

        # 配置区域
        config_group = QGroupBox("处理配置", self)
        config_layout = QVBoxLayout(config_group)

        # 自定义文件名模式
        pattern_layout = QHBoxLayout()
        self.use_custom_pattern_check = QCheckBox("使用自定义文件名解析模式", self)
        self.use_custom_pattern_check.setFont(default_font)
        self.use_custom_pattern_check.stateChanged.connect(self.toggle_pattern_entry)
        pattern_layout.addWidget(self.use_custom_pattern_check)

        # 预设模式选项
        preset_patterns = {
            "默认": r'^(\d{1,4}年\d{1,2}月\d{1,2}日|\d{1,2}月\d{1,2}日)[_\-](\d+\.?\d*)元[_\-]([^\.]+)\.pdf$',
            "日期_金额_姓名": r'^(\d{1,4}年\d{1,2}月\d{1,2}日|\d{1,2}月\d{1,2}日)_(\d+\.?\d*)元_([^\.]+)\.pdf$',
            "金额_姓名_日期": r'^(\d+\.?\d*)元_([^\.]+)_(\d{1,4}年\d{1,2}月\d{1,2}日|\d{1,2}月\d{1,2}日)\.pdf$',
            "简洁模式": r'^(\d{4}-\d{2}-\d{2})_(\d+\.?\d*)_([^\.]+)\.pdf$'
        }

        self.preset_pattern_combo = QComboBox(self)
        self.preset_pattern_combo.setFont(default_font)
        self.preset_pattern_combo.addItems(list(preset_patterns.keys()))
        self.preset_pattern_combo.currentTextChanged.connect(self.on_preset_select)
        pattern_layout.addWidget(self.preset_pattern_combo)

        self.pattern_entry = QLineEdit(self)
        self.pattern_entry.setFont(default_font)
        self.pattern_entry.setDisabled(True)
        pattern_layout.addWidget(self.pattern_entry)

        config_layout.addLayout(pattern_layout)

        # 日期过滤区域 - 优化布局使其更紧凑
        date_filter_layout = QHBoxLayout()
        self.use_date_filter_check = QCheckBox("按日期范围过滤", self)
        self.use_date_filter_check.setFont(default_font)
        self.use_date_filter_check.stateChanged.connect(self.toggle_date_filter)
        date_filter_layout.addWidget(self.use_date_filter_check)

        date_label = QLabel("日期范围:", self)
        date_label.setFont(default_font)
        date_filter_layout.addWidget(date_label)

        self.start_date_edit = QDateEdit(self)
        self.start_date_edit.setFont(default_font)
        self.start_date_edit.setCalendarPopup(True)
        self.start_date_edit.setDate(QDate.currentDate().addDays(-30))
        self.start_date_edit.setMinimumWidth(120)  # 调整日期选择框宽度
        date_filter_layout.addWidget(self.start_date_edit)

        to_label = QLabel("至", self)
        to_label.setFont(default_font)
        to_label.setMinimumWidth(20)  # 缩小"至"标签宽度
        date_filter_layout.addWidget(to_label)

        self.end_date_edit = QDateEdit(self)
        self.end_date_edit.setFont(default_font)
        self.end_date_edit.setCalendarPopup(True)
        self.end_date_edit.setDate(QDate.currentDate())
        self.end_date_edit.setMinimumWidth(120)  # 调整日期选择框宽度
        date_filter_layout.addWidget(self.end_date_edit)

        # 快捷日期筛选
        quick_filter_button = QToolButton(self)
        quick_filter_button.setText("筛选")  # 缩短按钮文本
        quick_filter_button.setPopupMode(QToolButton.InstantPopup)
        quick_filter_button.setFont(default_font)
        quick_filter_button.setMenu(self.create_quick_filter_menu())
        quick_filter_button.setMinimumWidth(60)  # 调整按钮宽度
        date_filter_layout.addWidget(quick_filter_button)

        config_layout.addLayout(date_filter_layout)

        # 导出选项
        export_layout = QHBoxLayout()
        self.export_csv_check = QCheckBox("导出为CSV文件", self)
        self.export_csv_check.setFont(default_font)
        export_layout.addWidget(self.export_csv_check)

        self.export_json_check = QCheckBox("导出为JSON文件", self)
        self.export_json_check.setFont(default_font)
        export_layout.addWidget(self.export_json_check)

        self.include_charts_check = QCheckBox("在Excel中包含图表", self)
        self.include_charts_check.setFont(default_font)
        export_layout.addWidget(self.include_charts_check)

        config_layout.addLayout(export_layout)

        top_layout.addWidget(config_group)

        # 数据预览区域
        preview_group = QGroupBox("数据预览与统计", self)
        preview_layout = QVBoxLayout(preview_group)

        # 数据统计摘要
        stats_layout = QHBoxLayout()
        self.stats_label = QLabel("数据统计: 未处理", self)
        self.stats_label.setFont(default_font)
        stats_layout.addWidget(self.stats_label)

        self.export_table_button = QPushButton("导出表格", self)
        self.export_table_button.setFont(button_font)
        self.export_table_button.clicked.connect(self.export_preview_table)
        self.export_table_button.setEnabled(False)
        stats_layout.addWidget(self.export_table_button)

        preview_layout.addLayout(stats_layout)

        # 分隔线
        line = QFrame(self)
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        preview_layout.addWidget(line)

        # 表格预览
        self.preview_table = QTableWidget(0, 3, self)
        self.preview_table.setFont(default_font)
        self.preview_table.setHorizontalHeaderLabels(["日期", "金额(元)", "报销人"])
        self.preview_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.preview_table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.preview_table.setSelectionBehavior(QTableWidget.SelectRows)
        preview_layout.addWidget(self.preview_table)

        preview_buttons_layout = QHBoxLayout()
        self.preview_button = QPushButton("预览数据", self)
        self.preview_button.setFont(button_font)
        self.preview_button.clicked.connect(self.preview_data)
        preview_buttons_layout.addWidget(self.preview_button)

        self.clear_preview_button = QPushButton("清除预览", self)
        self.clear_preview_button.setFont(button_font)
        self.clear_preview_button.clicked.connect(self.clear_preview)
        preview_buttons_layout.addWidget(self.clear_preview_button)

        preview_layout.addLayout(preview_buttons_layout)

        top_layout.addWidget(preview_group)

        # 处理按钮区域
        button_layout = QHBoxLayout()
        self.process_button = QPushButton("开始处理", self)
        self.process_button.setFont(button_font)
        self.process_button.clicked.connect(self.process_directory)
        button_layout.addWidget(self.process_button)

        top_layout.addLayout(button_layout)

        # 进度条
        self.progress_bar = QProgressBar(self)
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        top_layout.addWidget(self.progress_bar)

        main_layout.addLayout(top_layout)

        # 状态区域
        status_layout = QHBoxLayout()
        self.status_label = QLabel("准备就绪", self)
        self.status_label.setFont(default_font)
        status_layout.addWidget(self.status_label)

        main_layout.addLayout(status_layout)

        # 日志区域
        log_group = QGroupBox("处理日志", self)
        log_layout = QVBoxLayout(log_group)

        self.log_text = QTextEdit(self)
        self.log_text.setFont(default_font)
        self.log_text.setReadOnly(True)
        log_layout.addWidget(self.log_text)

        main_layout.addWidget(log_group)

        # 版权信息
        copyright_label = QLabel("© 2025 发票处理工具 | 程序员开发", self)
        copyright_label.setFont(default_font)
        main_layout.addWidget(copyright_label, alignment=Qt.AlignRight | Qt.AlignBottom)

        self.setLayout(main_layout)

        # 初始化导出路径为桌面
        desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
        self.output_entry.setText(desktop_path)

        # 初始化日期格式
        self.start_date_edit.setDisplayFormat("yyyy-MM-dd")
        self.end_date_edit.setDisplayFormat("yyyy-MM-dd")  # 改为更紧凑的日期格式

    def browse_folder(self):
        folder_path = QFileDialog.getExistingDirectory(self, "选择发票文件夹")
        if folder_path:
            self.folder_entry.setText(folder_path)
            # 如果导出路径为空，则自动设置为相同路径
            if not self.output_entry.text():
                self.output_entry.setText(folder_path)
            self.update_file_count()

    def browse_output_folder(self):
        folder_path = QFileDialog.getExistingDirectory(self, "选择导出路径")
        if folder_path:
            self.output_entry.setText(folder_path)

    def update_file_count(self):
        """更新文件夹中的PDF文件数量"""
        folder_path = self.folder_entry.text()
        if not folder_path or not os.path.isdir(folder_path):
            self.file_count_label.setText("文件数量: 0")
            return

        pdf_count = 0
        for root, dirs, files in os.walk(folder_path):
            pdf_count += len([f for f in files if f.lower().endswith('.pdf')])

        self.file_count_label.setText(f"文件数量: {pdf_count}")

    def toggle_pattern_entry(self):
        if self.use_custom_pattern_check.isChecked():
            self.pattern_entry.setDisabled(False)
        else:
            self.pattern_entry.setDisabled(True)

    def toggle_date_filter(self):
        self.start_date_edit.setEnabled(self.use_date_filter_check.isChecked())
        self.end_date_edit.setEnabled(self.use_date_filter_check.isChecked())

    def on_preset_select(self, text):
        preset_patterns = {
            "默认": r'^(\d{1,4}年\d{1,2}月\d{1,2}日|\d{1,2}月\d{1,2}日)[_\-](\d+\.?\d*)元[_\-]([^\.]+)\.pdf$',
            "日期_金额_姓名": r'^(\d{1,4}年\d{1,2}月\d{1,2}日|\d{1,2}月\d{1,2}日)_(\d+\.?\d*)元_([^\.]+)\.pdf$',
            "金额_姓名_日期": r'^(\d+\.?\d*)元_([^\.]+)_(\d{1,4}年\d{1,2}月\d{1,2}日|\d{1,2}月\d{1,2}日)\.pdf$',
            "简洁模式": r'^(\d{4}-\d{2}-\d{2})_(\d+\.?\d*)_([^\.]+)\.pdf$'
        }
        selected_pattern = preset_patterns.get(text, "")
        self.pattern_entry.setText(selected_pattern)

    def create_quick_filter_menu(self):
        """创建快捷日期筛选菜单"""
        menu = QMenu(self)

        # 今天
        today_action = QAction("今天", self)
        today_action.triggered.connect(lambda: self.set_date_range("today"))
        menu.addAction(today_action)

        # 昨天
        yesterday_action = QAction("昨天", self)
        yesterday_action.triggered.connect(lambda: self.set_date_range("yesterday"))
        menu.addAction(yesterday_action)

        # 本周
        this_week_action = QAction("本周", self)
        this_week_action.triggered.connect(lambda: self.set_date_range("this_week"))
        menu.addAction(this_week_action)

        # 上周
        last_week_action = QAction("上周", self)
        last_week_action.triggered.connect(lambda: self.set_date_range("last_week"))
        menu.addAction(last_week_action)

        # 本月
        this_month_action = QAction("本月", self)
        this_month_action.triggered.connect(lambda: self.set_date_range("this_month"))
        menu.addAction(this_month_action)

        # 上月
        last_month_action = QAction("上月", self)
        last_month_action.triggered.connect(lambda: self.set_date_range("last_month"))
        menu.addAction(last_month_action)

        # 本年
        this_year_action = QAction("本年", self)
        this_year_action.triggered.connect(lambda: self.set_date_range("this_year"))
        menu.addAction(this_year_action)

        return menu

    def set_date_range(self, range_type):
        """设置日期范围"""
        today = QDate.currentDate()

        if range_type == "today":
            self.start_date_edit.setDate(today)
            self.end_date_edit.setDate(today)
        elif range_type == "yesterday":
            self.start_date_edit.setDate(today.addDays(-1))
            self.end_date_edit.setDate(today.addDays(-1))
        elif range_type == "this_week":
            self.start_date_edit.setDate(today.addDays(-today.dayOfWeek() + 1))
            self.end_date_edit.setDate(today)
        elif range_type == "last_week":
            self.start_date_edit.setDate(today.addDays(-today.dayOfWeek() - 6))
            self.end_date_edit.setDate(today.addDays(-today.dayOfWeek()))
        elif range_type == "this_month":
            self.start_date_edit.setDate(QDate(today.year(), today.month(), 1))
            self.end_date_edit.setDate(today)
        elif range_type == "last_month":
            last_month = today.addMonths(-1)
            self.start_date_edit.setDate(QDate(last_month.year(), last_month.month(), 1))
            self.end_date_edit.setDate(QDate(last_month.year(), last_month.month(), last_month.daysInMonth()))
        elif range_type == "this_year":
            self.start_date_edit.setDate(QDate(today.year(), 1, 1))
            self.end_date_edit.setDate(today)

        # 启用日期过滤
        self.use_date_filter_check.setChecked(True)

    def create_date_filter(self):
        """创建日期过滤函数"""
        if not self.use_date_filter_check.isChecked():
            return None

        start_date = self.start_date_edit.date()
        end_date = self.end_date_edit.date()

        def date_filter(date_str):
            try:
                # 解析日期字符串
                if "年" in date_str and "月" in date_str and "日" in date_str:
                    year, month, day = map(int, re.findall(r'\d+', date_str))
                else:
                    # 处理类似"01-01"的日期格式，假设为当年
                    month, day = map(int, re.findall(r'\d+', date_str))
                    year = datetime.datetime.now().year

                # 转换为QDate进行比较
                file_date = QDate(year, month, day)
                return start_date <= file_date <= end_date
            except:
                return False  # 解析失败的日期不通过过滤

        return date_filter

    def preview_data(self):
        folder_path = self.folder_entry.text()
        if not folder_path or not os.path.isdir(folder_path):
            QMessageBox.warning(self, "警告", "请选择有效的文件夹路径！")
            return

        self.status_label.setText("正在预览数据...")
        self.progress_bar.setValue(0)
        self.preview_button.setEnabled(False)
        self.process_button.setEnabled(False)

        # 清除现有预览数据
        self.preview_table.setRowCount(0)

        # 创建日期过滤函数
        date_filter = self.create_date_filter()

        # 获取自定义模式
        custom_pattern = self.pattern_entry.text() if self.use_custom_pattern_check.isChecked() else None

        # 多线程预览数据
        self.preview_thread = ProcessingThread(folder_path, custom_pattern, date_filter)
        self.preview_thread.progress_updated.connect(self.update_preview_progress)
        self.preview_thread.processing_finished.connect(self.finish_preview_data)
        self.preview_thread.error_occurred.connect(self.handle_preview_error)
        self.preview_thread.start()

    def update_preview_progress(self, percentage, message):
        self.progress_bar.setValue(percentage)
        self.status_label.setText(message)

    def finish_preview_data(self, data):
        self.preview_thread = None
        self.progress_bar.setValue(100)

        if not data["records"]:
            QMessageBox.warning(self, "警告", "未找到可解析的发票文件！")
            self.status_label.setText("预览完成")
            self.preview_button.setEnabled(True)
            self.process_button.setEnabled(True)
            return

        # 显示预览数据
        self.preview_table.setRowCount(len(data["records"]))
        for row_idx, (date, amount, name) in enumerate(data["records"]):
            self.preview_table.setItem(row_idx, 0, QTableWidgetItem(date))
            self.preview_table.setItem(row_idx, 1, QTableWidgetItem(f"{amount:.2f}"))
            self.preview_table.setItem(row_idx, 2, QTableWidgetItem(name))

        # 更新统计信息
        self.update_stats(data)

        self.status_label.setText(f"预览完成，共 {len(data['records'])} 条数据")
        self.log_text.append(f"预览完成: {len(data['records'])} 条有效数据")
        self.export_table_button.setEnabled(True)
        self.preview_button.setEnabled(True)
        self.process_button.setEnabled(True)

    def handle_preview_error(self, error_msg):
        self.preview_thread = None
        self.progress_bar.setValue(0)
        self.status_label.setText("预览出错")
        self.log_text.append(f"\n{error_msg}")
        logging.error(error_msg)
        QMessageBox.critical(self, "预览错误", error_msg)
        self.preview_button.setEnabled(True)
        self.process_button.setEnabled(True)

    def update_stats(self, data):
        """更新数据统计信息"""
        if not data["records"]:
            self.stats_label.setText("数据统计: 无有效数据")
            return

        total_amount = sum(amount for _, amount, _ in data["records"])
        unique_dates = len(data["date_stats"])
        unique_people = len(data["person_stats"])

        self.stats_label.setText(
            f"数据统计: 共{len(data['records'])}条记录，合计{total_amount:.2f}元，{unique_dates}个日期，{unique_people}人")

    def clear_preview(self):
        self.preview_table.setRowCount(0)
        self.stats_label.setText("数据统计: 未处理")
        self.export_table_button.setEnabled(False)
        self.status_label.setText("准备就绪")

    def export_preview_table(self):
        """导出预览表格为CSV"""
        if self.preview_table.rowCount() == 0:
            QMessageBox.warning(self, "警告", "没有可导出的预览数据！")
            return

        folder_path = self.output_entry.text()
        if not folder_path:
            folder_path = os.path.join(os.path.expanduser("~"), "Desktop")

        output_path = os.path.join(folder_path, "预览数据.csv")
        counter = 1
        while os.path.exists(output_path):
            output_path = os.path.join(folder_path, f"预览数据_{counter}.csv")
            counter += 1

        try:
            with open(output_path, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.writer(f)
                # 写入表头
                writer.writerow([self.preview_table.horizontalHeaderItem(i).text() for i in
                                range(self.preview_table.columnCount())])
                # 写入数据
                for row in range(self.preview_table.rowCount()):
                    row_data = []
                    for col in range(self.preview_table.columnCount()):
                        item = self.preview_table.item(row, col)
                        row_data.append(item.text() if item else "")
                    writer.writerow(row_data)

            self.log_text.append(f"成功导出预览表格: {output_path}")
            QMessageBox.information(self, "导出成功", f"预览表格已导出至:\n{output_path}")
        except Exception as e:
            error_msg = f"导出预览表格时发生错误: {str(e)}"
            self.log_text.append(f"\n{error_msg}")
            logging.error(error_msg)
            QMessageBox.critical(self, "导出错误", error_msg)

    def process_directory(self):
        folder_path = self.folder_entry.text()
        if not folder_path or not os.path.isdir(folder_path):
            QMessageBox.warning(self, "警告", "请选择有效的文件夹路径！")
            return

        output_folder = self.output_entry.text()
        if not output_folder:
            output_folder = folder_path

        # 检查导出路径
        if not os.path.isdir(output_folder):
            try:
                os.makedirs(output_folder)
            except Exception as e:
                QMessageBox.warning(self, "警告", f"无法创建导出路径: {str(e)}")
                return

        self.status_label.setText("正在处理文件...")
        self.progress_bar.setValue(0)
        self.preview_button.setEnabled(False)
        self.process_button.setEnabled(False)
        self.export_table_button.setEnabled(False)

        # 创建日期过滤函数
        date_filter = self.create_date_filter()

        # 获取自定义模式
        custom_pattern = self.pattern_entry.text() if self.use_custom_pattern_check.isChecked() else None

        # 启动处理线程
        self.processing_thread = ProcessingThread(folder_path, custom_pattern, date_filter)
        self.processing_thread.progress_updated.connect(self.update_process_progress)
        self.processing_thread.processing_finished.connect(self.finish_processing)
        self.processing_thread.error_occurred.connect(self.handle_process_error)
        self.processing_thread.start()

    def update_process_progress(self, percentage, message):
        self.progress_bar.setValue(percentage)
        self.status_label.setText(message)

    def finish_processing(self, data):
        self.processing_thread = None
        self.data = data
        self.progress_bar.setValue(100)

        if not self.data["records"]:
            QMessageBox.warning(self, "警告", "未找到可解析的发票文件！")
            self.status_label.setText("处理完成")
            self.preview_button.setEnabled(True)
            self.process_button.setEnabled(True)
            return

        output_folder = self.output_entry.text()
        if not output_folder:
            output_folder = self.folder_entry.text()

        output_base = os.path.join(output_folder, "发票汇总")
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        output_base = f"{output_base}_{timestamp}"  # 添加时间戳避免文件覆盖

        # Excel报表
        excel_path = f"{output_base}.xlsx"
        create_excel_report(self.data, excel_path, self.include_charts_check.isChecked())
        self.log_text.append(f"成功生成Excel报表: {excel_path}")

        # CSV导出
        if self.export_csv_check.isChecked():
            csv_path = f"{output_base}.csv"
            export_to_csv(self.data, csv_path)
            self.log_text.append(f"成功导出CSV文件: {csv_path}")

        # JSON导出
        if self.export_json_check.isChecked():
            json_path = f"{output_base}.json"
            export_to_json(self.data, json_path)
            self.log_text.append(f"成功导出JSON文件: {json_path}")

        # 显示错误信息（如果有）
        if self.data["errors"]:
            error_msg = f"处理过程中发现 {len(self.data['errors'])} 个错误:\n" + "\n".join(self.data['errors'][:10])
            if len(self.data['errors']) > 10:
                error_msg += f"\n... 共{len(self.data['errors'])}个错误，详情见日志"
            self.log_text.append(f"\n{error_msg}")
            QMessageBox.warning(self, "部分错误", error_msg)

        # 更新统计信息
        self.update_stats(self.data)

        # 显示处理结果
        success_msg = (
            f"数据处理完成！\n\n"
            f"处理统计:\n"
            f"• 有效发票数量: {len(self.data['records'])}\n"
            f"• 合计金额: {sum(amount for _, amount, _ in self.data['records']):.2f}元\n"
            f"• 涉及日期: {len(self.data['date_stats'])}\n"
            f"• 涉及人员: {len(self.data['person_stats'])}\n\n"
            f"文件已保存至: {output_folder}"
        )

        QMessageBox.information(self, "处理完成", success_msg)
        open_folder(output_folder)

        self.status_label.setText("处理完成")
        self.preview_button.setEnabled(True)
        self.process_button.setEnabled(True)
        self.export_table_button.setEnabled(True)

    def handle_process_error(self, error_msg):
        self.processing_thread = None
        self.progress_bar.setValue(0)
        self.status_label.setText("处理出错")
        self.log_text.append(f"\n{error_msg}")
        logging.error(error_msg)
        QMessageBox.critical(self, "处理错误", error_msg)
        self.preview_button.setEnabled(True)
        self.process_button.setEnabled(True)

    def closeEvent(self, event):
        """程序关闭时的清理工作"""
        if self.processing_thread and self.processing_thread.isRunning():
            self.processing_thread.terminate()
            self.processing_thread.wait()
        event.accept()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    tool = InvoiceProcessingTool()
    tool.show()
    sys.exit(app.exec())